<?php
/**
 * uTorrent Bridge: A Modular uTorrent WebUI Bridge
 *
 * Provides uTorrent client support for non-native server backends.
 *
 * @version		0.1.2
 * @package		bridge
 * @copyright	Copyright (c) 2009,
 * 				Ryon Sherman <ryon.sherman@gmail.com>
 * @license		http://www.gnu.org/licenses/gpl.txt The GNU Public License
 */

/**
 * Server URL
 *
 * Connection string of non-native server to bridge.
 *
 * @internal	Include trailing forward-slash.
 */
define('SERVER_URL', 'http://localhost/');

/**
 * Server Module
 *
 * Module used for server interaction.
 *
 * @see			utb_server_interface
 * @internal 	Modules located in "./servers/*"
 */
define('SERVER_MODULE', 'rtorrent');

/**
 * Log File
 *
 * Location of log file.
 *
 * @internal	Relative or absolute path
 */
define('FILE_LOG', 'log.txt');

########################################################
###!!! !!! BELOW THIS POINT: DEVELOPERS ONLY! !!! !!!###
########################################################

/**
 * uTorrent Build
 *
 * Last supported uTorrent Build.
 *
 * @internal	Integer value required
 */
define('UTORRENT_BUILD', 16150);

/**
 * uTorrent Boolean: Disabled
 *
 * Integer value used by uTorrent to represent a "Disabled" boolean value.
 */
define('UTORRENT_BOOLEAN_DISABLED', -1);

/**
 * uTorrent Boolean: False
 *
 * Integer value used by uTorrent to represent a "False" boolean value.
 */
define('UTORRENT_BOOLEAN_FALSE', 0);

/**
 * uTorrent Boolean: True
 *
 * Integer value used by uTorrent to represent a "True" boolean value.
 */
define('UTORRENT_BOOLEAN_TRUE', 1);

/**
 * uTorrent Data Type: Integer
 *
 * Integer value used by uTorrent to represent an "Integer" data type.
 */
define('UTORRENT_DATA_TYPE_INTEGER', 0);

/**
 * uTorrent Data Type: Boolean
 *
 * Integer value used by uTorrent to represent a "Boolean" data type.
 */
define('UTORRENT_DATA_TYPE_BOOLEAN', 1);

/**
 * uTorrent Data Type: String
 *
 * Integer value used by uTorrent to represent a "String" data type.
 */
define('UTORRENT_DATA_TYPE_STRING', 2);

/**
 * uTorrent Status: Started
 *
 * Integer value used by uTorrent to represent a "Started" status.
 */
define('UTORRENT_STATUS_STARTED', 1);

/**
 * uTorrent Status: Checking
 *
 * Integer value used by uTorrent to represent a "Checking" status.
 */
define('UTORRENT_STATUS_CHECKING', 2);

/**
 * uTorrent Status: Check Before Starting
 *
 * Integer value used by uTorrent to represent a "Check Before Starting" status.
 */
define('UTORRENT_STATUS_CHECK_START', 4);

/**
 * uTorrent Status: Checked
 *
 * Integer value used by uTorrent to represent a "Checked" status.
 */
define('UTORRENT_STATUS_CHECKED', 8);

/**
 * uTorrent Status: Error
 *
 * Integer value used by uTorrent to represent an "Error" status.
 */
define('UTORRENT_STATUS_ERROR', 16);

/**
 * uTorrent Status: Paused
 *
 * Integer value used by uTorrent to represent a "Paused" status.
 */
define('UTORRENT_STATUS_PAUSED', 32);

/**
 * uTorrent Status: Queued
 *
 * Integer value used by uTorrent to represent a "Queued" status.
 */
define('UTORRENT_STATUS_QUEUED', 64);

/**
 * uTorrent Status: Loaded
 *
 * Integer value used by uTorrent to represent a "Loaded" status.
 */
define('UTORRENT_STATUS_LOADED', 128);

/**
 * uTorrent Priority: Exclude
 *
 * Integer value used by uTorrent to represent an "Exclude" priority.
 */
define('UTORRENT_PRIORITY_EXCLUDE', 0);

/**
 * uTorrent Priority: Low
 *
 * Integer value used by uTorrent to represent a "Low" priority.
 */
define('UTORRENT_PRIORITY_LOW', 1);

/**
 * uTorrent Priority: Normal
 *
 * Integer value used by uTorrent to represent a "Normal" priority.
 */
define('UTORRENT_PRIORITY_NORMAL', 2);

/**
 * uTorrent Priority: High
 *
 * Integer value used by uTorrent to represent a "High" priority.
 */
define('UTORRENT_PRIORITY_HIGH', 3);

# Disable script timeout.
set_time_limit(false);

# Disable error reporting.
# Any non-JSON output will crash clients.
error_reporting(false);

# Set custom error handler.
set_error_handler('utb_error_handler');

# Start service.
new utb_bridge;

/**
 * Custom Error Handler
 *
 * Logs PHP errors to file.
 *
 * @author	ryon.sherman
 *
 * @param	integer	$error_level	Level of error
 * @param	string	$error_string	Error message
 * @param	string	$error_file		File where error occured
 * @param	integer	$error_line		Line where error occured
 *
 * @return	false					Continue to default error handler
 */
function utb_error_handler($error_level, $error_string, $error_file, $error_line)
{
	# Log entry.
	utb_bridge::log($error_string.": [".$error_line."] ".$error_file, $error_level);

	# Continue to default error handler.
	# Default error output is squelched by error_reporting().
	return false;
}

/**
 * Bridge Class
 *
 * Provides methods for client/server interaction.
 *
 * @package	bridge
 */
class utb_bridge
{
	/**
	 * Client Address
	 *
	 * Address of connected client.
	 *
	 * @access		private
	 *
	 * @var			string
	 */
	private $_client;

	/**
	 * Client Username
	 *
	 * Username used for server authentication.
	 *
	 * @access	private
	 *
	 * @var		string
	 */
	private $_user;

	/**
	 * Client Password
	 *
	 * Password used for server authentication.
	 *
	 * @access	private
	 *
	 * @var		string
	 */
	private $_pass;

	/**
	 * Server Module
	 *
	 * Server module container.
	 *
	 * @access	private
	 *
	 * @var 	string
	 */
	private $_server;

	/**
	 * Client Data
	 *
	 * Client data returned in JSON format.
	 *
	 * @internal	uTorrent build number required
	 *
	 * @access		private
	 *
	 * @var			array
	 */
	private $_data = array('build' => UTORRENT_BUILD);

	/**
	 * Bridge Class Constructor
	 *
	 * Performs user authentication, server actions,
	 * and renders JSON output.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @return	string 	JSON encoded client data
	 */
	public function __construct()
	{
		# Authenticate client.
		$this->_auth_client();

		# Attach to server.
		$this->_attach_server();

		# Parse client request.
		$this->_parse_request();

    	# Output JSON encoded data.
		echo json_encode($this->_data);
	}

	/**
	 * Authenticate Client
	 *
	 * Performs user credential acquisition for server
	 * authentication.
	 *
	 * @author	ryon.sherman
	 * @access	private
	 */
	private function _auth_client()
	{
		# TODO: Refactor: toggle Basic HTTP authentication requirement.

		# Assign client address.
		$this->_client = $_SERVER['REMOTE_ADDR'];

		# Check if username is provided.
		if ( ! empty($_SERVER['PHP_AUTH_USER']))
		{
			# Assign username.
			$this->_user = $_SERVER['PHP_AUTH_USER'];
		}

		# Check if password is provided.
		if ( ! empty($_SERVER['PHP_AUTH_PW']))
		{
			# Assign password.
			$this->_pass = $_SERVER['PHP_AUTH_PW'];
		}

		# Check if username or password are not assigned.
		if (empty($this->_user) or empty($this->_pass))
		{
			# Send authentication headers to client.
			header('WWW-Authenticate: Basic realm="uTorrent"');
			header('HTTP/1.0 401 Unauthorized');

			# Terminate service since access is unauthorized.
			utb_bridge::quit('TEMP: no auth');
		}
	}

	/**
	 * Attach Server Module
	 *
	 * Locates and attaches the server module to it's container.
	 *
	 * @author	ryon.sherman
	 * @access	private
	 */
	private function _attach_server()
	{
		# Format module name.
		$module = strtolower(SERVER_MODULE);

		# Check if module exists.
		if (file_exists('../servers/'.$module.'.php'))
		{
			# Include server module.
			require_once('../servers/'.$module.'.php');
		}

		# Format class name.
		$class = 'utb_server_'.$module;

		# Check if server module implements server interface.
		if (in_array('utb_server_interface', class_implements($class)))
		{
			# Attach server module.
			$this->_server = new $class($this->_user, $this->_pass);
		}
		else
		{
			# Terminate service since server module is invalid.
			utb_bridge::quit('TEMP: no class', 'Invalid server module: '.$class);
		}
	}

	/**
	 * Parse Client Request
	 *
	 * Parses client request and determines server actions.
	 *
	 * @author	ryon.sherman
	 * @access	private
	 */
	private function _parse_request()
	{
		# Check if client is requesting list of transfers.
		if (! empty($_REQUEST['list']))
		{
			# Build list of transfers.
			$this->_list_transfers();
		}

		# Check if client is performing an action.
		if (! empty($_REQUEST['action']))
		{
			# Perform action
			$this->_perform_action(strtolower($_REQUEST['action']));
		}
	}

	/**
	 * List Transfers
	 *
	 * Lists current transfers on the server. Caching is used
	 * to reduce network load.
	 *
	 * @author	ryon.sherman
	 * @access	private
	 *
	 * @todo	Support transfer labels.
	 */
	private function _list_transfers()
	{
		# Assign cache id from client request if provided.
		$cache_id = ( ! empty($_REQUEST['cid'])) ? $_REQUEST['cid'] : null;

		# Retreive list of transfers.
		$transfers = $this->_server->get_transfers();

		# Write cache and get new cache id.
		$new_cache_id = $this->_write_cache($transfers);

		# TODO: Implement: transfer labels.
		# Add 'label' placeholder to client data.
		$this->_data['label'] = array();

		# Determine if cache should be used.
		if ( ! empty($cache_id))
		{
			# Read cache.
			$cache = $this->_read_cache($cache_id);

			# Create array of transfers modified since corresponding cache id.
			$this->_data['torrentp'] = array();
			foreach ($transfers as $hash => $transfer)
			{
				# Determine if hash does not exist in cache.
				if ( ! in_array($hash, array_keys($cache)))
				{
					# Add serialized transfer object to client data.
					$this->_data['torrentp'][] = $transfer->serialize();
				}
				elseif (is_object($cache[$hash]))
				{
					# Check if transfer has changed since cache.
					if (array_diff($transfer->serialize(), $cache[$hash]->serialize()))
					{
						# Add serialized transfer object to client data.
						$this->_data['torrentp'][] = $transfer->serialize();
					}
				}
			}

			# Create array of transfers removed since corresponding cache id.
			$this->_data['torrentm'] = array();
			foreach ($cache as $hash => $transfer)
			{
				# Check if cached hash does not exist in current transfers.
				if ( ! in_array($hash, array_keys($transfers)))
				{
					# Add hash to client data.
					$this->_data['torrentm'][] = $hash;
				}
			}
		}
		else
		{
			# Prepare array of current transfers.
			$this->_data['torrents'] = array();

			# Check if transfers are valid.
			if ( ! empty($transfers))
			{
				foreach ($transfers as $transfer)
				{
					# Add serialized transfer object to client data.
					$this->_data['torrents'][] = $transfer->serialize();
				}
			}
		}

		# Add new cache id to client data.
		$this->_data['torrentc'] = $new_cache_id;
	}

	/**
	 * Perform Server Action
	 *
	 * Performs requested action on the server.
	 *
	 * @author	ryon.sherman
	 * @access	private
	 *
	 * @todo	Support file addition via HTTP POST
	 * @todo	Support file priority modification
	 * @todo	Support transfer property modification
	 * @todo	Support server setting modification
	 */
	private function _perform_action($action)
	{
		# Parse multiple hashes from request.
		$hashes = array();
		foreach (explode('&', $_SERVER['QUERY_STRING']) as $pair)
		{
			# Break key/value pairs.
			list($key, $value) = explode('=', $pair);

			# Check if key is a hash.
			if (strtolower($key) === 'hash')
			{
				# Format value.
				$value = strtolower($value);

				# Add hash to array if unique.
				if ( ! in_array($value, $hashes))
				{
					$hashes[] = $value;
				}
			}
		}

		# TODO: Implement: multiple simultaneous setting modifications.

		# Assign setting to modify if provided.
		# Also used at 'source' for 'add-url'.
		$setting = ( ! empty($_REQUEST['s'])) ? strtolower($_REQUEST['s']) : null;
		# Assign modified setting value if provided.
		$value = ( ! empty($_REQUEST['v'])) ? $_REQUEST['v'] : null;

		# TODO: Implement: multiple simultaneous file modification.

		# Assign file to modify if provided.
		$file = ( ! empty($_REQUEST['f'])) ? intval($_REQUEST['f']) : null;
		# Assign modified file priority if provided.
		$priority = ( ! empty($_REQUEST['p'])) ? intval($_REQUEST['p']) : null;

		# Determine action to perform.
		# All actions are executed withinin the server module.
    	switch ($action)
    	{
    		# Add file via HTTP POST.
    		case 'add-file':
    			# TODO: Implement: 'add-file' action.
    		break;

    		# Add file via URL.
    		case 'add-url':
    			# Check for valid source.
				if (empty($setting))
				{
					# Terminate service since source is invalid.
					utb_bridge::quit('invalid request', 'no source');
				}

				# Perform action.
				$this->_server->add_url($setting);
			break;

    		# Force-start transfer(s).
    		case 'forcestart':
    			# Check for valid array of hashes.
			 	if (empty($hashes))
			 	{
			 		# Terminate service since array of hashes is invalid.
			 		utb_bridge::quit('invalid request', 'no hashes');
			 	}

				# Perform action.
				$this->_server->force_start($hashes);
			break;

			# Retrieve files for transfer(s).
			case 'getfiles':
				# Check for valid array of hashes.
				if (empty($hashes))
				{
					# Terminate service since array of hashes is invalid.
					utb_bridge::quit('invalid request', 'no hashes');
				}

				# Perform action and add results to client data.
				$this->_data['files'] = $this->_server->get_files($hashes);
			break;

			# Retrieve properties for transfer(s).
			case 'getprops':
				# Check for valid array of hashes.
				if (empty($hashes))
				{
					# Terminate service since array of hashes is invalid.
					utb_bridge::quit('invalid request', 'no hashes');
				}

				# Perform action and add results to client data.
				$this->_data['props'] = $this->_server->get_properties($hashes);
			break;

			# Retrieve server settings.
			case 'getsettings':
				# TODO: Determine: required minimum settings to be returned.

				# Perform action and add results to array of client data.
				$this->_data['settings'] = $this->_server->get_settings();
			break;

			# Set server setting(s).
			case 'setsetting':
				# Check for valid setting and value.
				if (empty($setting) or empty($value))
				{
					# Quit since setting or value is invalid.
					utb_bridge::quit('invalid request', 'no setting/value');
				}

				# Perform action
				$this->_server->set_setting($setting, $value);
			break;

			# Pause transfer(s).
			case 'pause':
				# Check for valid array of hashes.
				if (empty($hashes))
				{
					# Quit since array of hashes is invalid.
					utb_bridge::quit('invalid request', 'no hashes');
				}

				# Perform action.
				$this->_server->pause($value);
			break;

			# Recheck the hash of transfer(s).
			case 'recheck':
				# Check for valid array of hashes.
				if (empty($hashes))
				{
					# Quit since array of hashes is invalid.
					utb_bridge::quit('invalid request', 'no hashes');
				}

				# Perform action.
				$this->_server->recheck($hashes);
			break;

			# Remove transfer(s).
			case 'remove':
				# Check for valid array of hashes.
				if (empty($hashes))
				{
					# Terminate service since array of hashes is invalid.
					utb_bridge::quit('invalid request', 'no hashes');
				}

				# Perform action.
				$this->_server->remove($hashes);
			break;

			# Remove transfer(s) and delete files.
			case 'removedata':
				# Check for valid array of hashes.
				if (empty($hashes))
				{
					# Terminate service since array of hashes is invalid.
					utb_bridge::quit('invalid request', 'no hashes');
				}

				# Perform action.
				$this->_server->remove_data($hashes);
			break;

			# Set file priority.
			case 'setprio':
				# TODO: Implement: 'setprio' action.
			break;

			# Set transfer properties.
			case 'setprops':
				# TODO: Implement: 'setprops' action.
			break;

    		# Start transfer(s).
    		case 'start':
    			# Check for valid array of hashes.
				if (empty($hashes))
				{
					# Terminate service since array of hashes is invalid.
					utb_bridge::quit('invalid request', 'no hashes');
				}

				# Perform action.
    			$this->_server->start($hashes);
    		break;

    		# Stop transfer(s).
    		case 'stop':
    			# Check for valid array of hashes.
				if (empty($hashes))
				{
					# Terminate service since array of hashes is invalid.
					utb_bridge::quit('invalid request', 'no hashes');
				}

				# Perform action.
    			$this->_server->stop($hashes);
    		break;

			# Unpause transfer(s).
    		case 'unpause':
    			# Check for valid array of hashes.
				if (empty($hashes))
				{
					# Terminate service since array of hashes is invalid.
					utb_bridge::quit('invalid request', 'no hashes');
				}

				# Perform action.
    			$this->_server->unpause($hashes);
    		break;

			# Perform no action.
    		default:
    			# Terminate service since no action was specified.
				utb_bridge::quit('invalid request', 'no action');
    		break;
    	}
	}

	/**
	 * Read Cache
	 *
	 * Reads cached data from a temporary file.
	 *
	 * @author	ryon.sherman
	 * @access	private
	 *
	 * @param	string	$cache_id	Id of cached file
	 *
	 * @return	mixed	$data		Cached data
	 */
	private function _read_cache($cache_id)
	{
		# Format file name.
		$file = sys_get_temp_dir().'/uTorrent_Bridge_Cache_'.$cache_id;

		# Check if file exists.
		if ( ! file_exists($file))
		{
			# TODO: Refactor: prevent failure when cache not found

			# Terminate service since cache was not found.
			utb_bridge::quit('invalid request', 'no cache');
		}

		# Read data from file.
		$data = file_get_contents($file);

		# Unserialize data.
		$data = unserialize($data);

		# TODO: Determine: if cache file needs to be cleared after each use.
		# Delete cache file.
		# unlink($file);

		# Return cached data.
		return $data;
	}

	/**
	 * Write Cache
	 *
	 * Writes data to a temporary cache file.
	 *
	 * @author	ryon.sherman
	 * @access	private
	 *
	 * @param	mixed	$data		Data to cache
	 *
	 * @return	string	$cache_id	New cache Id
	 */
	private function _write_cache($data)
	{
		# Assign cache id.
		$cache_id = date('YmdHis');

		# Format file name.
		$file = sys_get_temp_dir().'/uTorrent_Bridge_Cache_'.$cache_id;

		# Serialize data.
		$data = serialize($data);

		# Write data to file.
		file_put_contents($file, $data);

		# Return new cache id.
		return $cache_id;
	}

	/**
	 * Log Message
	 *
	 * Logs messages to file.
	 *
	 * @see		E_USER_ERROR
	 * @see		E_USER_WARNING
	 * @see		E_USER_ERROR
	 *
	 * @author	ryon.sherman
	 * @access	public
	 * @static
	 *
	 * @param	string	$message		Log entry
	 * @param	integer	$error_level	Error level
	 */
	public static function log($message, $error_level = null)
	{
		# Determine error level
		switch($error_level)
		{
			# Fatal level.
			case E_USER_ERROR:
				$error_level = 'FATAL';
			break;

			# Warning level.
			case E_USER_WARNING:
				$error_level = 'WARNING';
			break;

			# Notice level.
			case E_USER_ERROR:
				$error_level = 'NOTICE';
			break;

			# No level.
			default:
				$error_level = null;
			break;
		}

		# Strip HTML tags from PHP errors.
		$message = strip_tags($message);

		# TODO: Refactor: log entry: include client address.

		# Format log entry.
		$entry = date('M d Y H:i:s').": {$error_level} {$message}\r\n";

		# Check if log should be written.
		$file = FILE_LOG;
		if ( ! empty($file))
		{
			# Write entry to log.
			file_put_contents($file, $entry, FILE_APPEND);
		}
	}

	/**
	 * Terminate Service
	 *
	 * Terminates the script after logging.
	 *
	 * @internal	Default uTorrent message is 'invalid request'
	 *
	 * @author	ryon.sherman
	 * @access	public
	 * @static
	 *
	 * @param	string	$client_message	Last message sent to client
	 * @param	string	$log_entry		Last log entry
	 */
	public static function quit($client_message = 'invalid request', $log_entry = null)
	{
		# Check if log entry should be written.
		if ( ! empty($log_entry))
		{
			# Log script termination.
			utb_bridge::log("Service Terminated. ".$log_entry);
		}

		# Output last message.
		echo $client_message;

		# Terminate script.
		exit();
	}
}

/**
 * Server Interface
 *
 * Contains the abstract methods required for
 * valid server modules.
 *
 * @package		bridge
 * @subpackage	server
 */
interface utb_server_interface
{
	/**
	 * Get Transfer Files Action
	 *
	 * Retrieves files for transfer(s).
	 *
	 * @see		utb_file
	 *
	 * @access	public
	 *
	 * @param	array	$hashes		Array of transfer hashes
	 *
	 * @return	array				Array of utb_file objects
	 */
	public function get_files($hashes);

	/**
	 * Get Transfer Properties Action
	 *
	 * Retrieves properties for transfer(s).
	 *
	 * @see		utb_properties
	 *
	 * @access	public
	 *
	 * @param	array	$hashes		Array of transfer hashes
	 *
	 * @return	array				Array of utb_properties objects
	 */
	public function get_properties($hashes);

	/**
	 * Get Server Settings Action
	 *
	 * Retrieves server settings.
	 *
	 * @access	public
	 *
	 * @return	array				Array of server settings and values
	 */
	public function get_settings();

	/**
	 * Get Transfers Action
	 *
	 * Retrieves transfers.
	 *
	 * @see		utb_transfer
	 *
	 * @access	public
	 *
	 * @return	array				Array of utb_transfer objects
	 */
	public function get_transfers();

	/**
	 * Set File Priority Action
	 *
	 * Sets the priority of file(s).
	 *
	 * @access	public
	 *
	 * @param	string	$hash		Transfer hash
	 * @param	integer	$file		File to modify
	 * @param	integer	$priority	Modified file priority
	 */
	public function set_priority($hash, $file, $priority);

	/**
	 * Set Transfer Property Action
	 *
	 * Sets the properties of transfer(s).
	 *
	 * @access	public
	 *
	 * @param	string	$hash		Transfer hash
	 * @param	string	$property	Property to modify
	 * @param	string	$value		Modified property value
	 */
	public function set_property($hash, $property, $value);

	/**
	 * Set Server Setting Action
	 *
	 * Sets server setting(s).
	 *
	 * @access	public
	 *
	 * @param	string	$setting	Setting to modify
	 * @param	string	$value		Modified setting value
	 */
	public function set_setting($setting, $value);

	/**
	 * Add Torrent File Action
	 *
	 * Adds a torrent via HTTP POST.
	 *
	 * @access	public
	 */
	public function add_file();

	/**
	 * Add Torrent URL Action
	 *
	 * Adds a torrent via URL.
	 *
	 * @access	public
	 *
	 * @param	string	$url		URL to add
	 */
	public function add_url($url);

	/**
	 * Force Start Transfer(s) Action
	 *
	 * Force-Starts transfer(s).
	 *
	 * @access	public
	 *
	 * @param	array	$hashes		Array of transfer hashes
	 */
	public function force_start($hashes);

	/**
	 * Pause Transfer(s) Action
	 *
	 * Pauses transfer(s).
	 *
	 * @access	public
	 *
	 * @param	array	$hashes		Array of transfer hashes
	 */
	public function pause($hashes);

	/**
	 * Recheck Transfer Hash(es) Action
	 *
	 * Rechecks the hash of transfer(s).
	 *
	 * @access	public
	 *
	 * @param	array	$hashes		Array of transfer hashes
	 */
	public function recheck($hashes);

	/**
	 * Remove Transfer(s) Action
	 *
	 * Removes transfer(s).
	 *
	 * @access	public
	 *
	 * @param	array	$hashes		Array of transfer hashes
	 */
	public function remove($hashes);

	/**
	 * Remove Transfer(s) With Data Action
	 *
	 * Removes transfer(s) and delete files.
	 *
	 * @access	public
	 *
	 * @param	array	$hashes		Array of transfer hashes
	 */
	public function remove_data($hashes);

	/**
	 * Restart Transfer(s) Action
	 *
	 * Restarts transfer(s).
	 *
	 * @access	public
	 *
	 * @param	array	$hashes		Array of transfer hashes
	 */
	public function restart($hashes);

	/**
	 * Start Transfer(s) Action
	 *
	 * Starts transfer(s).
	 *
	 * @access	public
	 *
	 * @param	array	$hashes		Array of transfer hashes
	 */
	public function start($hashes);

	/**
	 * Stop Transfer(s) Action
	 *
	 * Stops transfer(s).
	 *
	 * @access	public
	 *
	 * @param	array	$hashes		Array of transfer hashes
	 */
	public function stop($hashes);

	/**
	 * Unpause Transfer(s) Action
	 *
	 * Un-Pauses transfer(s).
	 *
	 * @access	public
	 *
	 * @param	array	$hashes		Array of transfer hashes
	 */
	public function unpause($hashes);
}

/**
 * Transfer Container
 *
 * Contains methods for utilizing a single transfer.
 *
 * @package		bridge
 * @subpackage	transfer
 */
class utb_transfer
{
	/**
	 * Transfer Hash
	 *
	 * Hash used for transfer identification.
	 *
	 * @internal	String value required
	 *
	 * @access	public
	 *
	 * @var		string
	 */
	public $hash;

	/**
	 * Transfer Status
	 *
	 * The bitwise value that is obtained by adding
	 * the values of corresponding statuses.
	 *
	 * @see 		UTORRENT_STATUS_CHECK_START
	 * @see 		UTORRENT_STATUS_CHECKED
	 * @see 		UTORRENT_STATUS_CHECKING
	 * @see 		UTORRENT_STATUS_ERROR
	 * @see 		UTORRENT_STATUS_LOADED
	 * @see 		UTORRENT_STATUS_PAUSED
	 * @see 		UTORRENT_STATUS_QUEUED
	 * @see 		UTORRENT_STATUS_STARTED
	 * @internal	Integer (bitwise status) value required
	 * @internal	Defaults to UTORRENT_STATUS_LOADED
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $status = UTORRENT_STATUS_LOADED;

	/**
	 * Transfer Name
	 *
	 * Name of transfer.
	 *
	 * @internal	String value required
	 * @internal	Defaults to empty string
	 *
	 * @access	public
	 *
	 * @var		string
	 */
	public $name = '';

	/**
	 * Total Transfer Size
	 *
	 * Total size of transfer.
	 *
	 * @internal	Integer (in bytes) value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $size = 0;

	/**
	 * Transfer Progress
	 *
	 * Transfer's current progress.
	 *
	 * @internal	Integer (in per mils) value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $progress = 0;

	/**
	 * Transfer Downloaded Size
	 *
	 * Size of downloaded data.
	 *
	 * @internal	Integer (in bytes) value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $size_downloaded = 0;

	/**
	 * Transfer Uploaded Size
	 *
	 * Size of uploaded data.
	 *
	 * @internal	Integer (in bytes) value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $size_uploaded = 0;

	/**
	 * Transfer Ratio
	 *
	 * Transfer's current download to upload ratio.
	 *
	 * @internal	Integer (in 1/10th percent) value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $ratio = 0;

	/**
	 * Transfer Upload Rate
	 *
	 * Transfer's current upload speed.
	 *
	 * @internal	Integer (in bytes per second) value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $rate_upload = 0;

	/**
	 * Transfer Download Rate
	 *
	 * Transfer's current download speed.
	 *
	 * @internal	Integer (in bytes per second) value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $rate_download = 0;

	/**
	 * Transfer Completion ETA
	 *
	 * Transfer's estimated time to completion.
	 *
	 * @internal	Integer (in seconds) value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $eta = 0;

	/**
	 * Transfer Label
	 *
	 * Transfer's label used for grouping.
	 *
	 * @internal	String value required
	 * @internal	Defaults to empty string
	 *
	 * @access	public
	 *
	 * @var		string
	 */
	public $label = '';

	/**
	 * Transfer Peers
	 *
	 * Number of peers transfer is currently connected to.
	 *
	 * @internal	Integer value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $peers = 0;

	/**
	 * Total Transfer Peers
	 *
	 * Number of peers available for the transfer.
	 *
	 * @internal	Integer value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $peers_total = 0;

	/**
	 * Transfer Seeds
	 *
	 * Number of seeds transfer is currently connected to.
	 *
	 * @internal	Integer value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $seeds = 0;

	/**
	 * Total Transfer Seeds
	 *
	 * Number of seeds available for the transfer.
	 *
	 * @internal	Integer value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $seeds_total = 0;

	/**
	 * Transfer Availability
	 *
	 * Transfer's current availability.
	 * Determined by the amount of seeds/peers available to the transfer.
	 *
	 * @internal	Integer (in 1/65535ths) value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $availability = 0;

	/**
	 * Transfer Queue Position
	 *
	 * Transfer's current position in server's job queue.
	 *
	 * @internal	Integer value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $position = 0;

	/**
	 * Transfer Time Remaining
	 *
	 * Time remaining until transfer is completed.
	 *
	 * @internal	Integer (in bytes) value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $remaining = 0;

	/**
	 * Transfer Class Constructor
	 *
	 * Parses transfer data and assigns class values.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function __construct($data)
	{
		# Check if array of transfer data is valid
		if ( ! is_array($data))
		{
			# Terminate service since array of
			# property data is invalid.
			utb_bridge::quit('invalid request', 'no data');
		}

		# Parse array of transfer data and set class values.
		$this->set_hash($data);
		$this->set_status($data);
		$this->set_name($data);
		$this->set_size($data);
		$this->set_progress($data);
		$this->set_size_downloaded($data);
		$this->set_size_uploaded($data);
		$this->set_ratio($data);
		$this->set_rate_upload($data);
		$this->set_rate_download($data);
		$this->set_remaining($data);
		$this->set_eta($data);
		$this->set_label($data);
		$this->set_peers($data);
		$this->set_peers_total($data);
		$this->set_seeds($data);
		$this->set_seeds_total($data);
		$this->set_availability($data);
		$this->set_position($data);
	}

	/**
	 * Set Transfer Hash
	 *
	 * Parses array of transfer data and assigns the
	 * 'hash' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_hash($data)
	{
		# Check if array of transfer data contains a valid
		# 'hash' value.
		if ( ! empty($data['hash']))
		{
			# Assign 'hash' class value from array
			# of transfer data.
			$this->hash = $data['hash'];
		}
	}

	/**
	 * Set Transfer Status
	 *
	 * Parses array of transfer data and assigns the
	 * 'status' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_status($data)
	{
		# Check if array of transfer data contains a valid
		# 'status' value.
		if ( ! empty($data['status']))
		{
			# Assign 'status' class value from array
			# of transfer data.
			$this->status = $data['status'];
		}
	}

	/**
	 * Set Transfer Name
	 *
	 * Parses array of transfer data and assigns the
	 * 'name' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_name($data)
	{
		# Check if array of transfer data contains a valid
		# 'name' value.
		if ( ! empty($data['name']))
		{
			# Assign 'name' class value from array
			# of transfer data.
			$this->name = $data['name'];
		}
	}

	/**
	 * Set Transfer Size
	 *
	 * Parses array of transfer data and assigns the
	 * 'size' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_size($data)
	{
		# Check if array of transfer data contains a valid
		# 'size' value.
		if ( ! empty($data['size']))
		{
			# Assign 'size' class value from array
			# of transfer data.
			$this->size = $data['size'];
		}
	}

	/**
	 * Set Transfer Progress
	 *
	 * Parses array of transfer data and assigns the
	 * 'progress' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_progress($data)
	{
		# Check if array of transfer data contains a valid
		# 'progress' value.
		if ( ! empty($data['progress']))
		{
			# Assign 'progress' class value from array
			# of transfer data.
			$this->progress = $data['progress'];
		}
	}

	/**
	 * Set Transfer Downloaded Size
	 *
	 * Parses array of transfer data and assigns the
	 * 'size_downloaded' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_size_downloaded($data)
	{
		# Check if array of transfer data contains a valid
		# 'size_downloaded' value.
		if ( ! empty($data['size_downloaded']))
		{
			# Assign 'size_downloaded' class value from array
			# of transfer data.
			$this->size_downloaded = $data['size_downloaded'];
		}
	}

	/**
	 * Set Transfer Uploaded Size
	 *
	 * Parses array of transfer data and assigns the
	 * 'size_uploaded' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_size_uploaded($data)
	{
		# Check if array of transfer data contains a valid
		# 'size_uploaded' value.
		if ( ! empty($data['size_uploaded']))
		{
			# Assign 'size_uploaded' class value from array
			# of transfer data.
			$this->size_uploaded = $data['size_uploaded'];
		}
	}

	/**
	 * Set Transfer Ratio
	 *
	 * Parses array of transfer data and assigns the
	 * 'ratio' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_ratio($data)
	{
		# Check if array of transfer data contains a valid
		# 'ratio' value.
		if ( ! empty($data['ratio']))
		{
			# Assign 'ratio' class value from array
			# of transfer data.
			$this->ratio = $data['ratio'];
		}
	}

	/**
	 * Set Transfer Upload Rate
	 *
	 * Parses array of transfer data and assigns the
	 * 'rate_upload' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_rate_upload($data)
	{
		# Check if array of transfer data contains a valid
		# 'rate_upload' value.
		if ( ! empty($data['rate_upload']))
		{
			# Assign 'rate_upload' class value from array
			# of transfer data.
			$this->rate_upload = $data['rate_upload'];
		}
	}

	/**
	 * Set Transfer Download Rate
	 *
	 * Parses array of transfer data and assigns the
	 * 'rate_download' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_rate_download($data)
	{
		# Check if array of transfer data contains a valid
		# 'rate_download' value.
		if ( ! empty($data['rate_download']))
		{
			# Assign 'rate_download' class value from array
			# of transfer data.
			$this->rate_download = $data['rate_download'];
		}
	}

	/**
	 * Set Transfer Time Remaining
	 *
	 * Parses array of transfer data and assigns the
	 * 'remaining' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_remaining($data)
	{
		# Check if array of transfer data contains a valid
		# 'remaining' value.
		if ( ! empty($data['remaining']))
		{
			# Assign 'remaining' class value from array
			# of transfer data.
			$this->remaining = $data['remaining'];
		}
	}

	/**
	 * Set Transfer ETA
	 *
	 * Parses array of transfer data and assigns the
	 * 'eta' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_eta($data)
	{
		# Check if array of transfer data contains a valid
		# 'eta' value.
		if ( ! empty($data['eta']))
		{
			# Assign 'eta' class value from array
			# of transfer data.
			$this->eta = $data['eta'];
		}
	}

	/**
	 * Set Transfer Label
	 *
	 * Parses array of transfer data and assigns the
	 * 'label' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_label($data)
	{
		# Check if array of transfer data contains a valid
		# 'label' value.
		if ( ! empty($data['label']))
		{
			# Assign 'label' class value from array
			# of transfer data.
			$this->label = $data['label'];
		}
	}

	/**
	 * Set Transfer Peers
	 *
	 * Parses array of transfer data and assigns the
	 * 'peers' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_peers($data)
	{
		# Check if array of transfer data contains a valid
		# 'peers' value.
		if ( ! empty($data['peers']))
		{
			# Assign 'peers' class value from array
			# of transfer data.
			$this->peers = $data['peers'];
		}
	}

	/**
	 * Set Total Transfer Peers
	 *
	 * Parses array of transfer data and assigns the
	 * 'peers_total' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_peers_total($data)
	{
		# Check if array of transfer data contains a valid
		# 'peers_total' value.
		if ( ! empty($data['peers_total']))
		{
			# Assign 'peers_total' class value from array
			# of transfer data.
			$this->peers_total = $data['peers_total'];
		}
	}

	/**
	 * Set Transfer Seeds
	 *
	 * Parses array of transfer data and assigns the
	 * 'seeds' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_seeds($data)
	{
		# Check if array of transfer data contains a valid
		# 'seeds' value.
		if ( ! empty($data['seeds']))
		{
			# Assign 'seeds' class value from array
			# of transfer data.
			$this->seeds = $data['seeds'];
		}
	}

	/**
	 * Set Total Transfer Seeds
	 *
	 * Parses array of transfer data and assigns the
	 * 'seeds_total' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_seeds_total($data)
	{
		# Check if array of transfer data contains a valid
		# 'seeds_total' value.
		if ( ! empty($data['seeds_total']))
		{
			# Assign 'seeds_total' class value from array
			# of transfer data.
			$this->seeds_total = $data['seeds_total'];
		}
	}

	/**
	 * Set Transfer Availability
	 *
	 * Parses array of transfer data and assigns the
	 * 'availability' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_availability($data)
	{
		# Check if array of transfer data contains a valid
		# 'availability' value.
		if ( ! empty($data['availability']))
		{
			# Assign 'availability' class value from array
			# of transfer data.
			$this->availability = $data['availability'];
		}
	}

	/**
	 * Set Transfer Queue Order
	 *
	 * Parses array of transfer data and assigns the
	 * 'position' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of transfer data
	 */
	public function set_position($data)
	{
		# Check if array of transfer data contains a valid
		# 'position' value.
		if ( ! empty($data['position']))
		{
			# Assign 'position' class value from array
			# of transfer data.
			$this->position = $data['position'];
		}
	}

	/**
	 * Serialize Transfer Object Data
	 *
	 * Retrieves sorted array of class values.
	 *
	 * @internal	Used for JSON conversion
	 * @internal	Returned array is order sensitive
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @return	array				Array of class values
	 */
	public function serialize()
	{
		# Return sorted array of class values.
		return array(
			$this->hash,
			$this->status,
			$this->name,
			$this->size,
			$this->progress,
			$this->size_downloaded,
			$this->size_uploaded,
			$this->ratio,
			$this->rate_upload,
			$this->rate_download,
			$this->eta,
			$this->label,
			$this->peers,
			$this->peers_total,
			$this->seeds,
			$this->seeds_total,
			$this->availability,
			$this->position,
			$this->remaining,
		);
	}
}

/**
 * Transfer Properties Container
 *
 * Contains methods for utilizing a single set
 * of transfer properties.
 *
 * @package		bridge
 * @subpackage	property
 */
class utb_properties
{
	/**
	 * Transfer Hash Property
	 *
	 * Hash used for transfer identification.
	 *
	 * @internal	String value required
	 *
	 * @access	public
	 *
	 * @var		string
	 */
	public $hash;

	/**
	 * Transfer Trackers Property
	 *
	 * List of available trackers for transfer.
	 *
	 * @internal	String value required
	 * @internal	Seperated by '\r\n'.
	 *
	 * @access	public
	 *
	 * @var		string
	 */
	public $trackers;

	/**
	 * Transfer Upload Rate Property
	 *
	 * Speed in bytes per second transfer is allowed
	 * to attain.
	 *
	 * @internal	Integer (in bytes per second) value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $rate_upload = 0;

	/**
	 * Transfer Download Rate Property
	 *
	 * Speed in bytes per second transfer is allowed
	 * to attain.
	 *
	 * @internal	Integer (in bytes per second) value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $rate_download = 0;

	/**
	 * Transfer Super-seed Property
	 *
	 * Transfer uses super-seed feature.
	 *
	 * @see			UTORRENT_BOOLEAN_DISABLED
	 * @see			UTORRENT_BOOLEAN_FALSE
	 * @see			UTORRENT_BOOLEAN_TRUE
	 * @internal	Integer value required
	 * @internal	Defaults to UTORRENT_BOOLEAN_DISABLED
	 *
	 * @access	public
	 *
	 * @var		interger
	 */
	public $superseed = UTORRENT_BOOLEAN_DISABLED;

	/**
	 * Transfer DHT Property
	 *
	 * Transfer uses DHT feature.
	 *
	 * @see			UTORRENT_BOOLEAN_DISABLED
	 * @see			UTORRENT_BOOLEAN_FALSE
	 * @see			UTORRENT_BOOLEAN_TRUE
	 * @internal	Integer value required
	 * @internal	Defaults to UTORRENT_BOOLEAN_DISABLED
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $dht = UTORRENT_BOOLEAN_DISABLED;

	/**
	 * Transfer PEX Property
	 *
	 * Transfer uses PEX feature.
	 *
	 * @see			UTORRENT_BOOLEAN_DISABLED
	 * @see			UTORRENT_BOOLEAN_FALSE
	 * @see			UTORRENT_BOOLEAN_TRUE
	 * @internal	Integer value required
	 * @internal	Defaults to UTORRENT_BOOLEAN_DISABLED
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $pex = UTORRENT_BOOLEAN_DISABLED;

	/**
	 * Transfer Queue Override Property
	 *
	 * Transfer allowed to override queueing.
	 *
	 * @see			UTORRENT_BOOLEAN_DISABLED
	 * @see			UTORRENT_BOOLEAN_FALSE
	 * @see			UTORRENT_BOOLEAN_TRUE
	 * @internal	Integer value required
	 * @internal	Defaults to UTORRENT_BOOLEAN_DISABLED
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $override = UTORRENT_BOOLEAN_DISABLED;

	/**
	 * Transfer Seed Ratio Property
	 *
	 * Ratio at which transfer will stop seeding.
	 *
	 * @internal	Integer (in per mils) value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $seed_ratio = 0;

	/**
	 * Transfer Seed Time Property
	 *
	 * Minimum amount of time transfer is allowed to seed
	 * after downloading has finished.
	 *
	 * @internal	Integer (in seconds) value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $seed_time = 0;

	/**
	 * Transfer Upload Slots Property
	 *
	 * Maximum number of upload slots transfer is
	 * allowed to utilize.
	 *
	 * @internal	Integer value required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $upload_slots = 0;

	/**
	 * Properties Class Constructor
	 *
	 * Parses property data and assigns class values.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of property data
	 */
	public function __construct($data)
	{
		# Check if array of property data is valid
		if ( ! is_array($data))
		{
			# Terminate service since array of
			# property data is invalid.
			utb_bridge::quit('invalid request', 'no data');
		}

		# Parse array of property data and set class values.
		$this->set_hash($data);
		$this->set_trackers($data);
		$this->set_rate_upload($data);
		$this->set_rate_download($data);
		$this->set_superseed($data);
		$this->set_dht($data);
		$this->set_pex($data);
		$this->set_override($data);
		$this->set_seed_ratio($data);
		$this->set_seed_time($data);
		$this->set_upload_slots($data);
	}

	/**
	 * Set Transfer Hash Property
	 *
	 * Parses array of property data and assigns the
	 * 'hash' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of property data
	 */
	public function set_hash($data)
	{
		# Check if array of property data contains a valid
		# 'hash' value.
		if ( ! empty($data['hash']))
		{
			# Assign 'hash' class value from array
			# of property data.
			$this->hash = $data['hash'];
		}
	}

	/**
	 * Set Transfer Trackers Property
	 *
	 * Parses array of property data and assigns the
	 * 'trackers' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of property data
	 */
	public function set_trackers($data)
	{
		# Check if array of property data contains a valid
		# 'trackers' value.
		if ( ! empty($data['trackers']))
		{
			# Determine if 'trackers' value is an array.
			if (is_array($data['trackers']))
			{
				# Concatenate trackers array from array of property data
				# and assign 'trackers' class value.
				$this->trackers = implode("\r\n", $data['trackers']);
			}
			else
			{
				# Assign 'trackers' class value from array
				# of property data.
				$this->trackers = $data['trackers'];
			}
		}
	}

	/**
	 * Set Transfer Upload Rate Property
	 *
	 * Parses array of property data and assigns the
	 * 'rate_upload' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of property data
	 */
	public function set_rate_upload($data)
	{
		# Check if array of property data contains a valid
		# 'rate_upload' value.
		if ( ! empty($data['rate_upload']))
		{
			# Assign 'rate_upload' class value from array
			# of property data.
			$this->rate_upload = $data['rate_upload'];
		}
	}

	/**
	 * Set Transfer Download Rate Property
	 *
	 * Parses array of property data and assigns the
	 * 'rate_download' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of property data
	 */
	public function set_rate_download($data)
	{
		# Check if array of property data contains a valid
		# 'rate_download' value.
		if ( ! empty($data['rate_download']))
		{
			# Assign 'rate_download' class value from array
			# of property data.
			$this->rate_download = $data['rate_download'];
		}
	}

	/**
	 * Set Transfer Super-seed Property
	 *
	 * Parses array of property data and assigns the
	 * 'superseed' class value.
	 *
	 * @see		utb_properties::superseed
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of property data
	 */
	public function set_superseed($data)
	{
		# Check if array of property data contains a valid
		# 'superseed' value.
		if ( ! empty($data['superseed']))
		{
			# Assign 'superseed' class value from array
			# of property data.
			$this->superseed = $data['superseed'];
		}
	}

	/**
	 * Set Transfer DHT Property
	 *
	 * Parses array of property data and assigns the
	 * 'dht' class value.
	 *
	 * @see		utb_properties::dht
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of property data
	 */
	public function set_dht($data)
	{
		# Check if array of property data contains a valid
		# 'dht' value.
		if ( ! empty($data['dht']))
		{
			# Assign 'dht' class value from array
			# of property data.
			$this->dht = $data['dht'];
		}
	}

	/**
	 * Set Transfer PEX Property
	 *
	 * Parses array of property data and assigns the
	 * 'pex' class value.
	 *
	 * @see		utb_properties::pex
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of property data
	 */
	public function set_pex($data)
	{
		# Check if array of property data contains a valid
		# 'pex' value.
		if ( ! empty($data['pex']))
		{
			# Assign 'pex' class value from array
			# of property data.
			$this->pex = $data['pex'];
		}
	}

	/**
	 * Set Transfer Queue Override Property
	 *
	 * Parses array of property data and assigns the
	 * 'override' class value.
	 *
	 * @see		utb_properties::override
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of property data
	 */
	public function set_override($data)
	{
		# Check if array of property data contains a valid
		# 'override' value.
		if ( ! empty($data['override']))
		{
			# Assign 'override' class value from array
			# of property data.
			$this->override = $data['override'];
		}
	}

	/**
	 * Set Transfer Seed Ratio Property
	 *
	 * Parses array of property data and assigns the
	 * 'seed_ratio' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of property data
	 */
	public function set_seed_ratio($data)
	{
		# Check if array of property data contains a valid
		# 'seed_ratio' value.
		if ( ! empty($data['seed_ratio']))
		{
			# Assign 'seed_ratio' class value from array
			# of property data.
			$this->seed_ratio = $data['seed_ratio'];
		}
	}

	/**
	 * Set Transfer Seed Time Property
	 *
	 * Parses array of property data and assigns the
	 * 'seed_time' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of property data
	 */
	public function set_seed_time($data)
	{
		# Check if array of property data contains a valid
		# 'seed_time' value.
		if ( ! empty($data['seed_time']))
		{
			# Assign 'seed_time' class value from array
			# of property data.
			$this->seed_time = $data['seed_time'];
		}
	}

	/**
	 * Set Transfer Upload Slots Property
	 *
	 * Parses array of property data and assigns the
	 * 'upload_slots' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of property data
	 */
	public function set_upload_slots($data)
	{
		# Check if array of property data contains a valid
		# 'upload_slots' value.
		if ( ! empty($data['upload_slots']))
		{
			# Assign 'upload_slots' class value from array
			# of property data.
			$this->upload_slots = $data['upload_slots'];
		}
	}

	/**
	 * Serialize Properties Object Data
	 *
	 * Retrieves sorted array of class values.
	 *
	 * @internal	Used for JSON conversion
	 * @internal	Returned array is order sensitive
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @return	array				Array of class values
	 */
	public function serialize()
	{
		# Return sorted array of class values.
		return array(
			'hash' 			=> $this->hash,
			'trackers' 		=> $this->trackers,
			'ulrate' 		=> $this->rate_upload,
			'dlrate' 		=> $this->rate_download,
			'superseed' 	=> $this->superseed,
			'dht' 			=> $this->dht,
			'pex' 			=> $this->pex,
			'seed_override' => $this->override,
			'seed_ratio' 	=> $this->seed_ratio,
			'seed_time' 	=> $this->seed_time,
			'ulslots' 		=> $this->upload_slots,
		);
	}
}

/**
 * File Container
 *
 * Contains methods for utilizing a single transfer file.
 *
 * @package		bridge
 * @subpackage	file
 */
class utb_file
{
	/**
	 * File Name
	 *
	 * Name of file.
	 *
	 * @internal	String value required
	 *
	 * @access	public
	 *
	 * @var		string
	 */
	public $name;

	/**
	 * Total File Size
	 *
	 * Total size of file in bytes.
	 *
	 * @internal	Integer value (in bytes) required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $size = 0;

	/**
	 * Downloaded File Size
	 *
	 * Downloaded size of file in bytes.
	 *
	 * @internal	Integer value (in bytes) required
	 * @internal	Defaults to 0
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $size_downloaded = 0;

	/**
	 * File Priority
	 *
	 * Priority of file.
	 *
	 * @see			UTORRENT_PRIORITY_EXCLUDE
	 * @see			UTORRENT_PRIORITY_LOW
	 * @see			UTORRENT_PRIORITY_NORMAL
	 * @see			UTORRENT_PRIORITY_HIGH
	 * @internal	Integer value required
	 * @internal	Defaults to UTORRENT_PRIORITY_LOW
	 *
	 * @access	public
	 *
	 * @var		integer
	 */
	public $priority = UTORRENT_PRIORITY_LOW;

	/**
	 * File Class Constructor
	 *
	 * Parses file data and assigns class values.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of file data
	 */
	public function __construct($data)
	{
		# Check if array of file data is valid
		if ( ! is_array($data))
		{
			# Terminate service since array of
			# file data is invalid.
			utb_bridge::quit('invalid request', 'no data');
		}

		# Parse array of file data and set class values.
		$this->set_name($data);
		$this->set_size($data);
		$this->set_size_downloaded($data);
		$this->set_priority($data);
	}

	/**
	 * Set File Name
	 *
	 * Parses array of file data and assigns the
	 * 'name' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of file data
	 */
	public function set_name($data)
	{
		# Check if array of file data contains a valid
		# 'name' value.
		if ( ! empty($data['name']))
		{
			# Assign 'name' class value from array
			# of file data.
			$this->name = $data['name'];
		}
	}

	/**
	 * Set Total File Size
	 *
	 * Parses array of file data and assigns the
	 * 'size' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data		Array of file data
	 */
	public function set_size($data)
	{
		# Check if array of file data contains a valid
		# 'size' value.
		if ( ! empty($data['size']))
		{
			# Assign 'size' class value from array
			# of file data.
			$this->size = $data['size'];
		}
	}

	/**
	 * Set Downloaded File Size
	 *
	 * Parses array of file data and assigns the
	 * 'size_downloaded' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data	Array of file data
	 */
	public function set_size_downloaded($data)
	{
		# Check if array of file data contains a valid
		# 'size_downloaded' value.
		if ( ! empty($data['size_downloaded']))
		{
			# Assign 'size_downloaded' class value from array
			# of file data.
			$this->size_downloaded = $data['size_downloaded'];
		}
	}

	/**
	 * Set File Priority
	 *
	 * Parses array of file data and assigns the
	 * 'priority' class value.
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @param	array	$data	Array of file data
	 */
	public function set_priority($data)
	{
		# Check if array of file data contains a valid
		# 'priority' value.
		if ( ! empty($data['priority']))
		{
			# Assign 'priority' class value from array
			# of file data.
			$this->priority = $data['priority'];
		}
	}

	/**
	 * Serialize File Object Data
	 *
	 * Retrieves sorted array of class values.
	 *
	 * @internal	Used for JSON conversion
	 * @internal	Returned array is order sensitive
	 *
	 * @author	ryon.sherman
	 * @access	public
	 *
	 * @return	array				Array of class values
	 */
	public function serialize()
	{
		# Return sorted array of class values.
		return array(
			$this->name,
			$this->size,
			$this->size_downloaded,
			$this->priority,
		);
	}
}
?>